package orm

import (
	"bytes"
	"errors"
	"strconv"
	"reflect"
)

type QuerySet interface {
	And(string, ...interface{}) QuerySet
	AndNot(string,...interface{})QuerySet
	Columns(columns ...string) QuerySet
	OrderAscBy(rowsName ...string) QuerySet
	OrderDescBy(rowsName ...string) QuerySet
	SetOffset(offset int) QuerySet
	SetLimit(limit int) QuerySet
	Execute(...interface{}) error

}

var(
	DefaultLimit = 1000
)

type querySet struct {
	GetValue  interface{}
	ModelInfo *ModelInfo
	Conditions *Condition
	SelectColumns []string
	Limit int
	Offset int
	Group []string
	Orders []string
	OrderDirection string
	Sql bytes.Buffer
	err error
}

func (qs *querySet)Columns(columns ...string) QuerySet {
	if qs.err !=nil {
		return qs
	}

	if columns == nil || len(columns)<=0{
		return qs
	}

	for _,column := range columns{
		qs.SelectColumns = append(qs.SelectColumns,column)
	}
	return qs
}

func (qs *querySet)And(expr string, args...interface{}) QuerySet{
	if qs.err !=nil {
		return qs
	}

	if expr == "" || args == nil || len(args)<=0{
		qs.err = errors.New("expr is empty or value is nil")
	}

	qs.Conditions = qs.Conditions.And(expr,&qs.err,args...)
	return  qs
}

func (qs *querySet)AndNot(expr string,args ...interface{}) QuerySet{
	if qs.err !=nil {
		return qs
	}

	if expr == "" || args == nil || len(args)<=0{
		qs.err = errors.New("expr is empty or value is nil")
	}

	qs.Conditions = qs.Conditions.AndNot(expr,&qs.err,args...)
	return qs
}

func (qs *querySet)OrderAscBy(rowsName ...string) QuerySet{
	if qs.err !=nil {
		return qs
	}

	if rowsName == nil || len(rowsName) == 0{
		return qs
	}

	for _,rn := range rowsName{
		qs.Orders = append(qs.Orders,rn)
	}
	qs.OrderDirection = " ASC  "
	return qs
}

func (qs *querySet)OrderDescBy(rowsName ...string) QuerySet {
	if qs.err !=nil {
		return qs
	}

	if rowsName == nil || len(rowsName) == 0{
		return qs
	}

	for _,rn := range rowsName{
		qs.Orders = append(qs.Orders,rn)
	}
	qs.OrderDirection = " DESC  "

	return qs
}

func (qs *querySet)SetOffset(offset int) QuerySet {
	if qs.err !=nil {
		return qs
	}

	if qs.err!=nil{
		return qs
	}
	if offset<0{
		qs.err = errors.New("offset must >= 0")
		return qs
	}

	qs.Offset = offset
	return qs
}

func (qs *querySet)SetLimit(limit int) QuerySet {
	if qs.err !=nil {
		return qs
	}

	if qs.err!=nil{
		return qs
	}

	if limit <0 {
		qs.err = errors.New("limit must >=0 ")
		return qs
	}

	qs.Limit = limit
	return qs
}

func (qs *querySet)Execute(arg ...interface{}) error {
	if qs.err!=nil{
		return qs.err
	}

	if  len(arg)>1  {
		return errors.New("receiver is err")
	}

	if len(arg)==1 {
		refArg := reflect.TypeOf(arg[0])
		if refArg.Kind()!=reflect.Ptr{
			return errors.New("the afferent value must ptr")
		}
		qs.GetValue = arg[0]
	}

	switch qs.Sql.String() {
		case "SELECT":
			qs.getSelectSql()
			if err:=qs.doSelect();err!=nil{
				return err
			}
		case "UPDATE":
			return nil
		case "DELETE":
			qs.getDeleteSql()
			if err:=qs.doDelete();err!=nil{
				return err
			}
			return nil
		default:
			return errors.New("no sql operation(SELECT 、UPDATE or DELETE)")
	}


	return nil
}

func (qs *querySet)getSelectSql(){
	if qs.err!=nil{
		return
	}

	count := len(qs.SelectColumns)
	if count == 0{
		qs.Sql.WriteString(" * ")
	}else{
		for i,rowName := range qs.SelectColumns{
			qs.Sql.WriteString(" "+rowName)
			if i!=(count-1){
				qs.Sql.WriteString(",")
			}
		}
	}
	qs.Sql.WriteString(" FROM "+qs.ModelInfo.TableName)
	qs.getWhereSql()
	qs.setLimitAndOffset()
}

func (qs *querySet)getDeleteSql(){
	if qs.err!=nil{
		return
	}
	qs.Sql.WriteString(" FROM "+qs.ModelInfo.TableName)
	qs.getWhereSql()
}

func (qs *querySet)getWhereSql(){
	if qs.err!=nil{
		return
	}

	if len(qs.Conditions.Conds)==0{
		return
	}

	qs.Sql.WriteString(" WHERE ")
	i:=1
	for _,cond := range qs.Conditions.Conds{
		if i!=1 {
			qs.Sql.WriteString(cond.conj+" ")
		}

		qs.Sql.WriteString(cond.isNot)
		qs.Sql.WriteString(cond.ColumnName)

		if cond.Operator == "between" || cond.Operator == "BETWEEN"{
			qs.Sql.WriteString("$"+strconv.Itoa(i)+" AND ")
			i++
			qs.Sql.WriteString("$"+strconv.Itoa(i)+" ")
			i++
			continue
		}

		if cond.Operator == "in" || cond.Operator == "IN" {
			qs.Sql.WriteString("(")

			expectValueCount := len(cond.ExpectValue)
			for vn:=0;vn<expectValueCount;vn++{
				qs.Sql.WriteString("$"+strconv.Itoa(i))
				i++
				if vn!=expectValueCount-1{
					qs.Sql.WriteString(",")
				}
			}

			qs.Sql.WriteString(") ")
			continue
		}
		qs.Sql.WriteString(cond.Operator)
		qs.Sql.WriteString(" $"+strconv.Itoa(i))
		i++
	}

	ordersCount := len(qs.Orders)
	if ordersCount!=0{
		qs.Sql.WriteString(" ORDER BY ")
		for ri,_ := range qs.Orders{
			qs.Sql.WriteString("$"+strconv.Itoa(i))
			i++
			if ri!=ordersCount-1{
				qs.Sql.WriteString(",")
			}
		}
	}
}

func (qs *querySet)setLimitAndOffset(){
	if qs.err!=nil{
		return
	}
	qs.Sql.WriteString(" LIMIT "+strconv.Itoa(qs.Limit))
	qs.Sql.WriteString(" OFFSET "+strconv.Itoa(qs.Offset))
}

func (qs *querySet)doSelect()error{
	if qs.err!=nil{
		return qs.err
	}

	var values []interface{}

	for _,cond := range qs.Conditions.Conds{
		for _,v := range cond.ExpectValue{
			values = append(values,v)
		}
	}

	if err:=Orm.OrmDb.Select(qs.Sql.String(),qs,values...);err!=nil{
		return err
	}

	return nil
}

func (qs *querySet)doDelete() error{
	if qs.err!=nil{
		return qs.err
	}

	var values []interface{}

	for _,cond := range qs.Conditions.Conds{
		for _,v := range cond.ExpectValue{
			values = append(values,v)
		}
	}

	if err:=Orm.OrmDb.Delete(qs.Sql.String(),values...);err!=nil{
		return err
	}

	return nil
}

